package handler;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

@Controller
public class FileRequestMappingHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
            Exception arg3) {
        ModelAndView modelAndView = new ModelAndView("index");
        modelAndView.addObject("message", "文件上传长度总和超出限制");
        return modelAndView;
    }

    /*
     * 此方法对应的请求映射路径为“/upload”
     */
    @RequestMapping(path = "/upload")
    public ModelAndView uploadFile(
            /*
             * @RequestParam指示注解的方法参数应绑定的web请求参数
             * 此处表示方法参数uploadFiles绑定web请求参数files
             */
            @RequestParam(name = "files", required = false) CommonsMultipartFile[] uploadFiles, HttpServletRequest req)
            throws MaxUploadSizeExceededException {
        ModelAndView modelAndView = new ModelAndView("index");
        if (req.getMethod().equals("POST")) {
            for (MultipartFile file : uploadFiles) {
                // 获取原始文件名
                String filename = file.getOriginalFilename();
                // 设置文件存储路径
                String filePath = req.getServletContext().getRealPath("/") + "uploaded/" + filename;
                File saveFile = new File(filePath);
                // 如果文件存在则执行传输，否则先创建文件
                if (!saveFile.exists()) {
                    saveFile.mkdirs();
                }
                try {
                    // 将收到的文件传输到给定的目标文件
                    file.transferTo(saveFile);
                } catch (IllegalStateException | IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return modelAndView;
    }

    /*
     * @RequestMapping注解中的value等价于路径（path）；method参数表示方法，如get，post，put等
     */
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public String fileList() {
        return "list";
    }

    /*
     * RequestMapping的path或value属性可以包含参数，但参数名称必须被“{}”引用，
     * 且该参数须与@PathVariable注解的处理器方法参数相关联
     */
    @RequestMapping(path = "/download/{file.name}")
    public String downloadFile(
            // @PathVariable表示方法参数应该被绑定到URI模板变量的注解，name或value值必须等于@RequestMapping中的参数值
            @PathVariable(name = "file.name") String name, HttpServletRequest req, HttpServletResponse resp,
            Model model) throws IOException {
        name = req.getServletContext().getRealPath("/uploaded/") + name;
        File downloadFile = new File(name);
        if (!downloadFile.exists()) {
            model.addAttribute("error", "请求下载的文件不存在，可能它已被删除或移动至其他位置");
            return "list";
        }
        FileInputStream fis;
        OutputStream os;
        // 设置响应头部
        resp.setHeader("content-disposition",
                "attachment;filename=\"" + new String(downloadFile.getName().getBytes("utf-8"), "ISO-8859-1") + "\"");
        // 读取预下载的文件将其保存至文件输入流
        fis = new FileInputStream(name);
        // 创建输出流
        os = resp.getOutputStream();
        // 创建缓冲区，循环将文件输入流中的内容读取至缓冲区
        byte[] buffer = new byte[16384];
        int length;
        while (-1 != (length = fis.read(buffer))) {
            // 使用输出流将缓冲区中的内容输出至浏览器端
            os.write(buffer, 0, length);
        }
        os.close();
        fis.close();
        return null;
    }
}
